package com.errol.batchprocessor.batchprocessor.abs;

import com.renjia.service.generic.log.LogService;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.lang.NonNull;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;

/**
 * 抽象扩展点容器
 *
 * @author zhang xiao bin
 * @version v1.0
 * @date 2021/03/06
 * @since v5.0.5
 **/
@SuppressWarnings("unchecked")
public abstract class AbstractAdvisorContainer<AP extends AbstractAdvisorPayload>
        implements ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, AbstractAdvisor<AP> {

    /**
     * 所有可执行扩展点缓存
     */
    private final Map<Class<? extends AbstractAdvisor<AP>>, List<AbstractAdvisor<AP>>> advisorMap = new HashMap<>();
    /**
     * 以 AP batchPayload 为 key，为每个批次缓存其可执行的扩展点（shallProcess 为 true）
     */
    private final ConcurrentMap<AP, Map<Class<? extends AbstractAdvisor<AP>>, List<AbstractAdvisor<AP>>>> missionAdvisorMap = new ConcurrentHashMap<>();
    protected final LogService logService;
    private ApplicationContext applicationContext = null;

    public AbstractAdvisorContainer(LogService logService) {
        this.logService = logService;
    }

    @Override
    public final void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public final void onApplicationEvent(@NonNull ContextRefreshedEvent event) {
        if (this.applicationContext != null) {
            for (Class<?> advisor : this.getClass().getInterfaces()) {
                Class<? extends AbstractAdvisor<AP>> childOfAbstractAdvisor = findAdvisorClass(advisor);
                if (childOfAbstractAdvisor != null) {
                    advisorMap.putIfAbsent(childOfAbstractAdvisor, new ArrayList<>());
                    applicationContext.getBeansOfType(advisor).values().forEach(bean -> {
                        if (bean != this) {
                            advisorMap.get(childOfAbstractAdvisor).add((AbstractAdvisor<AP>) bean);
                        }
                    });
                    advisorMap.get(childOfAbstractAdvisor).sort(Comparator.comparingInt(AbstractAdvisor::priority));
                    if (!advisorMap.get(childOfAbstractAdvisor).isEmpty() && advisorMap.get(childOfAbstractAdvisor).get(0).priority() <= 0) {
                        throw new FatalBeanException(logService.warn("Advisor 的排名优先级不能小于 1：advisor=" + advisor.getName()));
                    }
                }
            }
            this.applicationContext = null;
        }
    }

    /**
     * 从实现类（spring bean）继承的接口，迭代向父类查找 AbstractAdvisor 的直接子类
     *
     * @param advisor 执行 setApplicationContext 方法的类实现的接口
     * @return null 表未找到
     */
    protected abstract Class<? extends AbstractAdvisor<AP>> findAdvisorClass(Class<?> advisor);

    /**
     * 获取任务对应的扩展点，如果是第一次获取，则初始化
     *
     * @param advisorPayload AP
     * @return 扩展点实现的 map
     */
    protected final Map<Class<? extends AbstractAdvisor<AP>>, List<AbstractAdvisor<AP>>> getAdvisorMap(AP advisorPayload) {
        Map<Class<? extends AbstractAdvisor<AP>>, List<AbstractAdvisor<AP>>> advisorMap = missionAdvisorMap.get(advisorPayload);
        if (advisorMap == null) {
            advisorMap = new HashMap<>(this.advisorMap.size());
            for (Map.Entry<Class<? extends AbstractAdvisor<AP>>, List<AbstractAdvisor<AP>>> entry : this.advisorMap.entrySet()) {
                advisorMap.put(entry.getKey(), entry.getValue().stream().filter(advisor -> advisor.shallProcess(advisorPayload)).collect(Collectors.toList()));
            }
            missionAdvisorMap.put(advisorPayload, advisorMap);
        }
        return advisorMap;
    }

    /**
     * 移除任务对应的预筛选的扩展点实现
     *
     * @param advisorPayload AP
     */
    protected final void removeAdvisorCacheForMission(AP advisorPayload) {
        missionAdvisorMap.remove(advisorPayload);
    }

    @Override
    public final boolean shallProcess(@NonNull AP advisorPayload) {
        return false;
    }

    @Override
    public final int priority() {
        return 0;
    }

    protected <A extends AbstractAdvisor<AP>> boolean doCallBool(AP advisorPayload, Class<A> aClass, AbstractAdvisorCaller<A> advisorCaller) {
        A a;
        for (AbstractAdvisor<AP> employeeAdvisor : getAdvisorMap(advisorPayload).get(aClass)) {
            a = (A) employeeAdvisor;
            if (!((boolean) advisorCaller.doCall(a))) {
                return false;
            }
        }
        return true;
    }
}
